﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Ors.RInterface.Engine;
using System.Windows.Forms;

namespace Ors.RInterface.Tests
{
    [TestFixture]
    public class Engine
    {
        REngine rEngine = null;
        double[] dVector = new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
        int[] iVector = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
        bool[] bVector = new bool[] { true, false, false, true };
        string[] sVector = new string[] { "italia", "francia", "grecia", "turchia", "romania" };
        DateTime[] dates = new DateTime[] { DateTime.Now, DateTime.Now + TimeSpan.FromSeconds(1), DateTime.Now + TimeSpan.FromSeconds(2) };
        DateTime Now = DateTime.Now;
        double[][] matrix = new double[][]
                {
                    new double[]{1,2,3}
                    ,new double[]{4,5,6}
                    ,new double[]{7,8,9}
                    ,new double[]{10,11,12}
                };
                int[][] imtx = new int[][]
                {
                    new int[]{1,2,3}
                    ,new int[]{4,5,6}
                    ,new int[]{7,8,9}
                    ,new int[]{10,11,12}
                };
               
        [SetUp]
        public void Setup()
        {

            rEngine = REngine.GetInstance(false);
            rEngine.WriteMessage += new EventHandler<MessageEventArgs>(rEngine_WriteMessage);
            rEngine.AskForInput += new EventHandler<AskUserEventArgs>(rEngine_AskForInput);
              rEngine.SetVar("var1", "ciao ciao")
                     .SetVar("var2", 16.0)
                     .SetVar("var3", 555)
                     .SetVar("avector", dVector)
                     .SetVar("ivector", iVector)
                     .SetVar("bvector", bVector )
                     .SetVar("svector", sVector)
                     .SetVar("stat1", TestDataProvider.GetDatasetTuples(), new string[] { }, 0)
                     .SetVar("Now", Now)
                     .SetVar("datevector", dates)
                     .SetVar("mtx", matrix)
                     .SetVar("imtx", imtx);
            

        }

        [Test]
        public void ExecuteASimplePortionOfCode()
        {
            rEngine.Execute(@"a<-1:10
                             b<-a*2"
                            
                );
            int[] res = rEngine.GetVar("a").AsIntegerArray();
            Assert.AreEqual(new int[]{1,2,3,4,5,6,7,8,9,10},res);
            double[] dRes = rEngine.GetVar("b").AsRealArray();
            Assert.AreEqual(new double[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }, dRes);
        }
        [Test]
        public void CreateAnIdentityMatrixAndCheckIt()
        {
            rEngine.Execute(@"mtx<-matrix(0,4,4)
                             diag(mtx)<-1
                             mtx<-mtx*2
                             "

                );
            double[][] res = rEngine.GetVar("mtx").AsMatrixOfReal();
            for (int i = 0; i < res[0].Length; ++i)
                for (int j = 0; j < res.Length; ++j)
                    if (i == j)
                        Assert.AreEqual(2, res[i][j]);
        }
        [Test]
        public void CreateABigIdentityMatrixAndCheckIt()
        {
            rEngine.Execute(@"mtx<-matrix(0,1000,1000)
                             diag(mtx)<-1
                            mtx<-mtx*2
                            "

                );
            double[][] res = rEngine.GetVar("mtx").AsMatrixOfReal();
            for (int i = 0; i < res[0].Length; ++i)
                for (int j = 0; j < res.Length; ++j)
                    if (i == j)
                        Assert.AreEqual(2, res[i][j]);
            
        }
        [Test]
        public void LoadAndDoSomeFancyStuffWithLibraryMass()
        {
            rEngine.Execute(@"library(MASS)
                             q<-painters$Expression
                            frame<-as.data.frame(painters)"

                );
            Array columns;
            Array rows;
            int[] res = rEngine.GetVar("q").AsIntegerArray();
            IEnumerable<object[]> frame = rEngine.GetVar("frame").AsDataFrame(out columns, out rows);
            
        }


        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedMatrixOfIntegerWithWrongType()
        {
            Assert.AreEqual(matrix, rEngine.GetVar("ivector").AsMatrixOfInteger());
        }

        [Test]
        public void GettingAPassedMatrixOfInteger()
        {
            Assert.AreEqual(matrix, rEngine.GetVar("imtx").AsMatrixOfInteger());
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedMatrixOfRealWithWrongType()
        {
            Assert.AreEqual(matrix, rEngine.GetVar("avector").AsMatrixOfReal());
        }

        [Test]
        public void GettingAPassedMatrixOfReal()
        {
            Assert.AreEqual(matrix, rEngine.GetVar("mtx").AsMatrixOfReal());
        }

        [Test]
        public void GettingAPassedStringValue()
        {
            Assert.AreEqual("ciao ciao", rEngine.GetVar("var1").AsString());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException),UserMessage="Type not compatible.")]
        public void GettingAPassedStringValueWithWrongType()
        {
            Assert.AreEqual("ciao ciao", rEngine.GetVar("var1").AsInteger());
        }

        [Test]
        public void GettingAPassedIntegerValue()
        {
            Assert.AreEqual(555, rEngine.GetVar("var3").AsInteger());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedIntegerValueWithWrongType()
        {
            Assert.AreEqual(555, rEngine.GetVar("var3").AsRealArray());
        }
        [Test]
        public void GettingAPassedRealArray()
        {
            Assert.AreEqual(dVector, rEngine.GetVar("avector").AsRealArray());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedRealArrayWithWrongType()
        {
            Assert.AreEqual(dVector, rEngine.GetVar("avector").AsDateTimeArray());
        }

        [Test]
        public void GettingAPassedIntegerArray()
        {
            Assert.AreEqual(iVector, rEngine.GetVar("ivector").AsIntegerArray());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedIntegerArrayWithWrongType()
        {
            Assert.AreEqual(iVector, rEngine.GetVar("ivector").AsLogicalArray());
        }

        [Test]
        public void GettingAPassedBoolArray()
        {
            Assert.AreEqual(bVector, rEngine.GetVar("bvector").AsLogicalArray());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedBoolArrayWithWrongType()
        {
            Assert.AreEqual(bVector, rEngine.GetVar("bvector").AsInteger());
        }

        [Test]
        public void GettingAPassedStringArray()
        {
            Assert.AreEqual(sVector, rEngine.GetVar("svector").AsStringArray());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedStringArrayWithWrongType()
        {
            Assert.AreEqual(sVector, rEngine.GetVar("svector").AsReal());
        }

        [Test]
        public void GettingAPassedDateTime()
        {
            Assert.AreEqual(Now.ToLongDateString(), rEngine.GetVar("Now").AsDateTime().ToLongDateString());
        }
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedDateTimeWithWrongType()
        {
            Assert.AreEqual(Now.ToLongDateString(), rEngine.GetVar("Now").AsLogicalScalar());
        }

        [Test]
        public void GettingAPassedDateTimeArray()
        {
            DateTime[] res = rEngine.GetVar("datevector").AsDateTimeArray();
            for( int i =0;i<res.Length;++i )
                Assert.AreEqual(dates[i].ToLongDateString(), res[i].ToLongDateString());
        }
        
        [Test]
        [ExpectedException(typeof(InvalidOperationException), UserMessage = "Type not compatible.")]
        public void GettingAPassedDateTimeArrayWithWrongType()
        {
            rEngine.GetVar("datevector").AsStringArray();
        }
        [Test]
        public void GettingAPassedDateFrame()
        {
            Array colNames,rowNames;
            IEnumerable<object[]> frame = rEngine.GetVar("stat1").AsDataFrame(out colNames,out rowNames);
            List<object[]> retrieved = new List<object[]>(frame);
            List<object[]> src = new List<object[]>(TestDataProvider.GetDatasetTuples());
            for (int i = 0; i < src.Count; ++i)
            {
                for (int j = 0; j < src[i].Length;++j )
                {
                    if (j == 0)
                    {
                        Assert.AreEqual(src[i][j], rowNames.GetValue(i));
                    }
                    else
                    {
                        if( null != src[i][j] )
                            Assert.AreEqual(src[i][j], retrieved[i][j - 1],string.Format("Error at {0}-{1}",i,j));
                    }
                }
            }
            for (int i = 1; i < src[0].Length; ++i)
            {
                Assert.AreEqual(string.Format("NONAME{0}", i), colNames.GetValue(i - 1));
            }
        }



        [TearDown]
        public void Release()
        {
            //rEngine.Dispose();
        }

        void rEngine_AskForInput(object sender, AskUserEventArgs e)
        {
            Assert.IsTrue(false, "This sould not happen in non-interactive mode");
        }

        void rEngine_WriteMessage(object sender, MessageEventArgs e)
        {
            Console.WriteLine(e.Message);
        }
        
    }


   
}
